
import chisel3._
import chisel3.util._
import chisel3.util.experimental._
import ID._

class Execution extends Module {
  val io = IO(new Bundle {
    val pc       = Input(UInt(32.W))
    val op_type  = Input(UInt(6.W))
    val rs1      = Input(UInt(64.W))
    val rs2      = Input(UInt(64.W))
    val src2     = Input(UInt(64.W))
    val fuType   = Input(UInt(3.W))  
    val imm      = Input(UInt(64.W))
    val csr_Jmp_addr = Input(UInt(32.W)) 
    val csr_Jmp_en   = Input(Bool())
    val csr_data = Input(UInt(64.W))  
    val mem_rdata= Input(UInt(64.W))  
    val br_en    = Output(Bool())
    val br_addr  = Output(UInt(32.W))    
    val rd_data  = Output(UInt(64.W))
  })

  val fence_i = (io.fuType === FuMou) 
  BoringUtils.addSource(fence_i, "fence_i")

  def Alu_access(rs1: UInt, rs2: UInt, imm:UInt, op_type: UInt): UInt = {
    val shamt = rs2(4, 0)
    val shamt64 = imm(5, 0)
    val sraw  = ((rs1(31,0).asSInt >> shamt).asUInt)
    MuxLookup(op_type, 0.U, Array(
      AluAdd  -> (rs1  +  rs2),
      AluSll  -> (rs1 << rs2(5, 0)),
      AluSlli -> (rs1 << shamt64),
      AluSlt  -> ((rs1.asSInt < rs2.asSInt).asUInt),
      AluSltu -> ((rs1 < rs2).asUInt),
      AluXor  -> (rs1  ^  rs2),
      AluSrl  -> (rs1  >> shamt),
      AluSrli -> (rs1  >> shamt64), 
      AluOr   -> (rs1  |  rs2),
      AluAnd  -> (rs1  &  rs2),
      AluSub  -> (rs1  -  rs2),
      AluLui  -> rs2,
      AluSra  -> ((rs1.asSInt >> shamt).asUInt),
      AluSrai -> ((rs1.asSInt >> shamt64).asUInt),      
      AluAddiw  -> Cat(Fill(32,(rs1 + rs2)(31)),(rs1 + rs2)(31,0)),
      AluSraw   -> Cat(Fill(32,sraw(31)), sraw(31,0)),
      AluSllw   -> Cat(Fill(32, (rs1(31,0) << shamt)(31)), (rs1(31,0) << shamt)(31,0)),
      AluSrlw   -> Cat(Fill(32, (rs1(31,0) >> shamt)(31)), (rs1(31,0) >> shamt)(31,0)),  
      AluSubw   -> Cat(Fill(32,(rs1 - rs2)(31)),(rs1 - rs2)(31,0))      
    ))
  }

  def Bru_en(isBru: Bool, pc: UInt, offset: UInt, rs1: UInt, rs2: UInt, op_type: UInt):Bool = {
    isBru && MuxLookup(op_type, false.B, Array(
      BruBeq  -> (rs1 === rs2),
      BruBne  -> (rs1 =/= rs2),
      BruBlt  -> (rs1.asSInt  <  rs2.asSInt),
      BruBge  -> (rs1.asSInt >=  rs2.asSInt),
      BruBltu -> (rs1  <  rs2),
      BruBgeu -> (rs1  >= rs2),
      BruJal  -> true.B,
      BruJalr -> true.B
    ))
  }

  val aluOut  = Alu_access(io.rs1, io.rs2, io.imm, io.op_type)
  val jmp_en   = Bru_en(io.fuType === FuBru, io.pc, io.rs2, io.rs1, io.src2, io.op_type)
  val jmp_addr = Mux(io.op_type === BruJalr, io.rs1 + io.rs2, io.pc + io.rs2)

  io.rd_data:= MuxLookup(io.fuType, 0.U(64.W), Array(
    FuAlu -> aluOut,
    FuBru -> (io.pc + 4.U),
  //  FuLsu -> Mux(io.clint_ren, io.clint_rdata, io.mem_rdata),
    FuLsu -> io.mem_rdata,
    FuCsr -> io.csr_data ,
    FuMou -> 0.U  
  ))

   when (fence_i) {
    io.br_en := true.B
    io.br_addr := io.pc + 4.U
   }
  .elsewhen (io.csr_Jmp_en) { 
    io.br_en := io.csr_Jmp_en 
    io.br_addr := io.csr_Jmp_addr}
  .otherwise { 
    io.br_en := jmp_en 
    io.br_addr := jmp_addr}
}
